package core;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import Classificadores.Classificadores;
import Classificadores.FiltroMelhoresClassificadores;
import Features.Feature;


public class Prototipo {

	int kfolds=5;
	int numNiveisHierarquia;
	String tipoHierarquia = "flat"; // default ou flat
	String dirDataSet = "treino";
	String modoFuncionamento = "pavelbm";
	Boolean fixedBranch=false;
	String featureList = new String();
	String classificadoresList = new String();
	String ngramas="500";
	
	public void init() throws Exception{
		
		Corpus corpus = new Corpus(dirDataSet);
		Feature features = new Feature(featureList, corpus);
		features.extractFeatures(corpus);
		
		Folds folds = new Folds(kfolds, dirDataSet);
		folds.init();
		Hierarquia hierarquia = new Hierarquia(numNiveisHierarquia);
		hierarquia.init(tipoHierarquia);		
		
				
		for(int a=0; a<hierarquia.niveisHierarquia.length; a++){
			
			ArrayList<Classificadores> melhoresClassificadores = new ArrayList<Classificadores>();  //NADA
			hierarquia.reduzHierarquiaNivel(corpus, a);
			for(int i=0; i<kfolds; i++){
				Simulacao simulacaoReal = new Simulacao(folds, i);
				
				//Determina thresholds para os classificadores
				if(modoFuncionamento.equals("beta")){
					melhoresClassificadores = determinaMelhoresClassificadores(simulacaoReal, corpus, hierarquia);
				}else{
					melhoresClassificadores = inicializaClassificadores();
				}
								
				Ficheiros files = new Ficheiros(simulacaoReal, corpus, hierarquia);
				files.init();
				Classificadores bestClassifiers = new Classificadores(melhoresClassificadores);
				bestClassifiers.usa(files, hierarquia, fixedBranch);
			}
			System.out.println("#####Nivel Hierarquico Actual = "+hierarquia.nivelHierarquicoActual+"#####");
			inicializaConfusionMatrix(melhoresClassificadores, corpus, hierarquia);
			
			Voting democracia = new Voting(corpus, hierarquia, 1);			
			melhoresClassificadores.add(democracia.exec());
			democracia = new Voting(corpus, hierarquia, 2);			
			melhoresClassificadores.add(democracia.exec());
			
			imprimeResultados(melhoresClassificadores);
			System.out.println();			
		}
		
	}
	
	
	public ArrayList<Classificadores> inicializaClassificadores(){
		Classificadores classificadores = new Classificadores(classificadoresList);
		ArrayList<Classificadores> lstClassificadores = classificadores.classifiersList;
		for (Classificadores it : lstClassificadores) {
			it.bestThreshold=-1;
		}
		return lstClassificadores;
	}
	
	
	
	public ArrayList<Classificadores> determinaMelhoresClassificadores(Simulacao simulacaoReal, Corpus corpus, Hierarquia hierarquia) throws Exception{
		
		Folds foldsThreshold = new Folds(kfolds, simulacaoReal.listaFicheirosTreino);
		foldsThreshold.init();
		ArrayList<Classificadores> classificadoresThreshold = new ArrayList<Classificadores>();
		for(int b=0; b<kfolds; b++){
			//trata dos ficheiros para treino e teste
			Simulacao simulacao = new Simulacao(foldsThreshold, b);
			//Gera ficheiros para weka
			Ficheiros files = new Ficheiros(simulacao, corpus, hierarquia);
			files.init();
			//Inicializa todos os classificadores
			Classificadores classificadores = new Classificadores(classificadoresList);
			//Determina melhor threshold de cada classificador
			classificadores.determinaThreshold(files);
			//Guarda todos os classificadores e thresholds desta fold na lista
			classificadoresThreshold.add(classificadores);
		}
		
		//Selecciona os melhores classificadores ja com threshold
		FiltroMelhoresClassificadores filtroClassificadores = new FiltroMelhoresClassificadores("bla bla bla -filtra=30");
		ArrayList<Classificadores> melhoresClassificadores = filtroClassificadores.filtra(classificadoresThreshold);
		return melhoresClassificadores;
	}
	
	public void imprimeResultados(ArrayList<Classificadores> lst){
		
		ArrayList<String> nomesClassificadores = new ArrayList<String>();
		for (Classificadores it : lst) {
			nomesClassificadores.add(it.myName);
		}
		
		double[] precisao = new double[nomesClassificadores.size()];
		double[] recall = new double[nomesClassificadores.size()];
		
		int i=0;
		for (Classificadores it : lst) {
			precisao[i]+=it.conf.precisao();
			recall[i]+=it.conf.recall();
			i++;
		}
					
		for(int a=0; a<nomesClassificadores.size(); a++){
			System.out.println(nomesClassificadores.get(a).toString());
			double P=precisao[a];
			double R=recall[a];
			System.out.println("Precisao = "+P);
			System.out.println("Recall = "+R);
			System.out.println("F1 = "+2*((P*R)/(P+R)));
			System.out.println();
			lst.get(a).conf.detailsByClass();
			lst.get(a).conf.imprimeMatrix();
			System.out.println();
			System.out.flush();
		}
		
	}
	
	public void inicializaConfusionMatrix(ArrayList<Classificadores> listaClassificadores, Corpus corpus, Hierarquia hierarquia){
		for (Classificadores it : listaClassificadores) {
			it.initConfusionMatrix(corpus, hierarquia);	
		}	
	}
	
	public String getArgument(String userInput, String argName){
		
		String[] parts = userInput.split(" ");
		String retorno = new String("");
		Boolean flag=false;
		for (String it : parts) {
			if(it.contains("-"+argName)){
				flag=true;
				continue;
			}
			if(flag==true){
				if(it.contains("-")){
					return retorno.trim();
				}else{
					retorno=retorno+" "+it.toString();
				}
			}
		}
		return retorno.trim();		
	}
	
	private void descreveUserInput() {
		
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Calendar cal = Calendar.getInstance();
		System.out.println("Hora de inicio -> "+ dateFormat.format(cal.getTime()));
		System.out.println("Numero de Folds = "+kfolds);
		System.out.println("Directorio de Dados = "+dirDataSet);
		System.out.println("Modo de funcionamento = "+modoFuncionamento);
		System.out.println("Tipo de Hierarquia = "+tipoHierarquia);
		System.out.println("Fixed Branch = "+fixedBranch.toString());
		System.out.println("Feature List = "+featureList);
		System.out.println("Numero Ngramas = "+ngramas);
		System.out.println("Classificadores usados = "+classificadoresList);
		System.out.flush();
	}
	
	
	public static void main(String [] args) throws Exception{
		
		Prototipo prototipo = new Prototipo();
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Calendar cal = Calendar.getInstance();
			
		String userInput = new String();
		for (String it : args) {
			userInput=userInput+it.toString()+" ";
		}
				
		prototipo.kfolds=userInput.contains("-kfolds")? Integer.parseInt(prototipo.getArgument(userInput, "kfolds")) : 5; 
		prototipo.dirDataSet=userInput.contains("-treino")? prototipo.getArgument(userInput, "treino") : "treino"; 
		prototipo.modoFuncionamento=userInput.contains("-modo")? prototipo.getArgument(userInput, "modo") : "bm"; //bm ou beta
		prototipo.tipoHierarquia=userInput.contains("-hierarquia")? prototipo.getArgument(userInput, "hierarquia") : "default"; //default ou flat
		prototipo.fixedBranch=userInput.contains("-fixed")? true : false; //true ou false
		prototipo.featureList=userInput.contains("-features")? prototipo.getArgument(userInput, "features") : "unigramas cap num abr quest exc ntok";  //"unigramas bigramas trigramas cap num abr quest exc ntok spos wne persons locais tempo vizinhos"
		prototipo.ngramas = userInput.contains("-ngramas")? prototipo.getArgument(userInput, "ngramas") : "500";
		prototipo.featureList=prototipo.featureList+" -ngramas="+prototipo.ngramas;
		prototipo.classificadoresList=userInput.contains("-classificadores")? prototipo.getArgument(userInput, "classificadores") : "all";  //nb, svm, crf, all
		
		String outputFile = new String(userInput.contains("-out")? prototipo.getArgument(userInput, "out") : new String(prototipo.kfolds+"_"+prototipo.modoFuncionamento+"_"+prototipo.featureList));
		System.out.println("OLAAAAAAAAAAA");
		System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile))));
		prototipo.descreveUserInput();
		
		if(prototipo.tipoHierarquia.equals("flat"))
			prototipo.numNiveisHierarquia=1;
		else
			prototipo.numNiveisHierarquia=3;
		prototipo.init();
		
		cal = Calendar.getInstance();
		System.out.println("Hora de fim -> "+ dateFormat.format(cal.getTime()));
		System.out.flush();
	}

}
